/*  8 maggio 2012
**  Classe: skype
**  invia messaggi provenienti da Dbus/skype al gestore e riceve tutte le comunicazioni
**  dal gestore/irc inviandole su Skype tramite la funzione invoke
*/

#include <iostream>
#include <string>
using namespace std;

//Librerie per l'uso delle socket
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
//Libreria di gestione delle stringhe
#include <string.h>

#include <glib.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>

#include <stdlib.h>
#include <stdio.h>

#include <time.h>





/* Costruttore che crea la connessione al dbus e si colllega all'oggetto di skype.*/
Skype :: Skype () : MAX_PACK(512), descr(des) {

  GMainLoop *loop;
  DBusError error;
  dbus_error_init (&error);
  DBusObjectPathVTable vtable;


  ttp->bus = dbus_bus_get (DBUS_BUS_SESSION, &error);

  if (dbus_error_is_set (&error))
      cerr << "[SKYPE-ERROR] Failed to get connection: " << error.message) << endl;


  if (!ttp->bus)
{
      cerr << "[SKYPE-ERROR] Failed to connect to D-BUS: "<< error.message << endl;
      dbus_error_free (&error);
      exit (1);
    }

  // Set up this connection to work in a GLib event loop.
  dbus_connection_setup_with_g_main (ttp->bus, NULL);

  loop = g_main_loop_new (NULL, FALSE);
  dbus_connection_setup_with_g_main (bus, NULL);        // set up te DBus connection
  
	// La funzione che viene evocata per i messaggi in arrivo da dbus
  vtable.message_function = notify_handler;      

	// Si collega a skype! (l'ultimo parametro è un puntatore ad una struct: 
	// sono i parametri che servono alla funzione
  dbus_connection_register_object_path (bus, "/com/Skype/Client", &vtable, NULL);

  // Register with Skype. Use the Skype API 1.2 protocol.
  invoke (bus, "NAME skype2irc", "OK", 20000);
  invoke (bus, "PROTOCOL 3", "OK", 500);
  count << "Skype: Terminato il buongiorno, entriamo nel loop!" << endl;
  
  g_main_loop_run (loop);
}

// questo riceve i messaggi da skype (i push che fa lui quando arrivano le notifiche)
// e il passa al gestore per vedere se sono messaggi!
static DBusHandlerResult Skype :: notify_handler (DBusConnection * bus, DBusMessage * msg, void *user_data)
{
	int irc_sd = (*(int *) user_data);
	//  printf("Skype: l'sd passato è %d\n", irc_sd);
	DBusMessageIter iter;
	dbus_message_iter_init (msg, &iter);	// occhio che devo strippare le strorfnzate e prendere la stringa!
	char *msg_from_skype;
	msg_from_skype = extract_reply_string (msg);
	gestisci_notifica (bus, msg_from_skype, irc_sd);
	return TRUE;
}



DBusMessage * Skype :: create_message (const char *arg)
{
	DBusMessage *message = dbus_message_new_method_call ("com.Skype.API",	// Sets the service the message should be sent to "com.Skype.API"
														 "/com/Skype",	// Sets the object path the message should be sent to "/com/Skype"
														 "com.Skype.API",	// Sets the interface to invoke method on to "com.Skype.API"
														 "Invoke");	// Sets the method to invoke to "Invoke"
	dbus_message_set_auto_start (message, TRUE);
	//dbus_message_set_sender (msg, "com.Skype.Client");  // necessario?
	dbus_message_append_args (message, DBUS_TYPE_STRING, &arg,	// se vogliamo fare i fighi e non siamo capaci..... &&&& :D
							  DBUS_TYPE_INVALID);
	return message;
}



/*
Questa è la funzione generica che invia un messaggio a skype tramite dbus.
*/
	
DBusMessage * Skype :: invoke (const char *cmd, const char *check,
        int time2wait)
{
  DBusMessage *send;
  DBusMessage *reply;
  const char *ack;
  DBusError errorz;
  dbus_error_init (&errorz);

  send = create_message (cmd);
  // printf("Bus: %p,\n send: %p,\n time2wait: %d,\n Error: %p\n", bus, send, time2wait, &errorz);  

  reply = dbus_connection_send_with_reply_and_block (bus, send, time2wait, &errorz);

  if (dbus_error_is_set (&errorz))
    {
      cerr << "[SKYPE-ERROR] Error: " << errorz.message << endl;
      dbus_error_free (&errorz);
      // exit (1);
    }

  if (reply == NULL)
    {
      // cout << "il reply è null" << endl;
    }
	/* Else: è tutto ok */
  else
    {
      dbus_message_get_args (reply, &errorz, DBUS_TYPE_STRING, &ack,
                             DBUS_TYPE_INVALID);
    }
	
  if (dbus_error_is_set (&errorz))
    {
      cerr << "[SKYPE-ERROR]" << (char *)&errorz.message << endl;
      dbus_error_free (&errorz);
    }


/* Questo serve se vogliamo implementare un check delle risposte di dbus
Praticamente sappiamo che se il messaggio che arriva da DBUS contiene una stringa nota, 
allora sappiamo che è corretto. In questo caso non sappiamo il contenuto delle stringhe di ritorno, quindi 
possiamo implementarlo eventualmente in un momento successivo */
/*
  if (check)
    {
      // Check that Skype's reply is that what expected to be
      if (strstr (check, ack) != 0);    //assert(0);
    }
  else
    {
      // Check for error from Skype itself
      if (strncmp ("ERROR", ack, 5) == 0);      //assert(0);
    }
 
  */
  // Non ho capito bene a cosa servono...
  //dbus_message_unref(send);
  //dbus_message_unref(reply);
  return (reply);
}

/* Il messaggio che mi è arrivato va letto, preso e inviato a sendIRC*/
int Skype :: gestisci_notifica (char *str, int irc_sd)
{
	strin from;		//mittente
	string body;			//messaggio
	DBusMessage *reply_raw;
	char *tmp;
	char * num;
	char * reply_msg;
	string messaggio;
	char buff[10]; // anche troppi messaggi :P
	
	if ((tmp = strstr (str, "RECEIVED")))
    {
		/*12 è la lunghezza di RECEIVED+BHO */
		char *inizio_num = str + 12;
		
		char *fine_num = strstr (inizio_num, " ");
		
		int diff = fine_num - inizio_num;

		/* Non sapendo di quanti caratteri è lungo il numero identificativo li skype, lo scopro così */
		num = memcpy (buff, inizio_num, fine_num-inizio_num); 
		buff[diff] = '\0';
		
		
		
		// Componiamo la query per trovare il body! 
		messaggio="GET CHATMESSAGE ";
		messaggio+=num;   //convertire num in stringa!
		messaggio+=" BODY";
		// invoke con il 3zo parametro di controllo risposta e un timeout di 1 secondo
		reply_raw = invoke (bus, stringa, "BODY", 1000);
		// passaggio da oggetto replydbus a stringa
		reply_msg = extract_reply_string (reply);	
		
		// il body si trova 5 caratteri dopo la prima occorrenza di body!
		if ((body = strstr (reply_msg, "BODY")))
		{
			body = body + 5;
			printf ("Messaggio: %s ", body);
		}
		
		// Componiamo la query per avere il mittente!
		// messaggio.empty();
		messaggio = "GET CHATMESSAGE ";
		messaggio += num;
		messaggio += " FROM_HANDLE";
		// invoke con il 3zo parametro di controllo risposta e un timeout di 1 secondo
		reply = invoke (bus, stringa, "FROM_HANDLE", 1000);
		// passaggio da oggetto replydbus a stringa
		reply_msg = extract_reply_string (reply);
		
		// il from si trova 5 caratteri dopo la prima occorrenza di body!
		if ((from = strstr (reply_msg, "FROM_HANDLE")))
		{
			from_handle = from_handle + 11;
			printf ("da %s\n", from_handle);
		}
		
		
		
		//GestoreBuffer.Send(irc_sd, from, body);
    }
	return 0;
}




/*
Procedimento per sapere come fare a mandare questo alla nostra chat preferita!
Troviamo le chat attive: SEARCH ACTIVECHATS || SEARCH MISSEDCHATS (l'ultimo funziona like a charm)

CHATMESSAGE #founder/$xxx.xxx;99d427e4ea32803f mittente: messaggio
*/
void Skype::Send (string nick, string msg)
{ 
  string skypemsg;
	skypemsg = "CHATMESSAGE ";
	skypemsg += globals.canale;
	skypemsg += " ";
	skypemsg += nick;
	skypemsg += ": ";
	skypemsg += msg;
	invoke (skypemsg, "SENDING", 1000);
}





string Skype::extract_reply_string (DBusMessage * msg)
{
	DBusMessageIter iter;
	int type;
	char *tmp;
	string str;
	
	dbus_message_iter_init (msg, &iter);
	type = dbus_message_iter_get_arg_type (&iter);
	
	if (type == DBUS_TYPE_INVALID)
		return (char *) 1;
	
	switch (type)
    {
			
		case DBUS_TYPE_STRING:	// it is a string message
		{
			dbus_message_iter_get_basic (&iter, &str);
			// printf ("Skype: Stringa ricevuta: %s\n", str);
			return str;
		}
			
		case DBUS_TYPE_VARIANT:	// another set of items
		{
			
			//   DBusMessageIter subiter;
			// dbus_message_iter_recurse( &iter, &subiter );
			extract_reply_string (msg);	// dovrebbe essere &subiter
			return 0;
		}
			
			
		default:
			;
    }
	
	return str;
}





